/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import { expect } from '@jest/globals';
import { copy, readdirSync, readJsonSync, statSync, writeFileSync } from 'fs-extra';
import * as os from 'os';
import { resolve } from 'path';

import { BuildDirConst } from '../../src/const/build-directory-const';
import { CommonConst } from '../../src/const/common-const';

import { appRoot, initByStageEmptyApp, initExecuteCommand, runHvigor, runShouldFail } from './common/common';
import { ModuleBuildProfileModifier } from './common/modifier/module-build-profile-modifier';
import { PackageJsonModifier } from './common/modifier/package-json-modifier';
import { Probes } from './common/probes';

const nativeOption = {
  'entry': {
    path: './src/main/cpp/CMakeLists.txt',
    arguments: '-v -DCMAKE_JOB_POOL_COMPILE:STRING=compile ' +
      '-DCMAKE_JOB_POOL_LINK:STRING=link -DCMAKE_JOB_POOLS:STRING=compile=2;link=2',
    abiFilters: [
      'armeabi-v7a',
      'arm64-v8a'
    ],
    cppFlags: ''
  },
  'library': {
    path: './src/main/cpp/CMakeLists.txt',
    arguments: '-v -DCMAKE_JOB_POOL_COMPILE:STRING=compile ' +
      '-DCMAKE_JOB_POOL_LINK:STRING=link -DCMAKE_JOB_POOLS:STRING=compile=2;link=2',
    abiFilters: [
      'armeabi-v7a',
      'arm64-v8a'
    ],
    cppFlags: ''
  },
  'library1': {
    path: './src/main/cpp/CMakeLists.txt',
    arguments: '-v -DCMAKE_JOB_POOL_COMPILE:STRING=compile ' +
      '-DCMAKE_JOB_POOL_LINK:STRING=link -DCMAKE_JOB_POOLS:STRING=compile=2;link=2',
    abiFilters: [
      'armeabi-v7a',
      'arm64-v8a'
    ],
    cppFlags: ''
  }
};

describe('Dependencies test', () => {
  test.concurrent('Stage ets assembleHap', async () => {
    await initByStageEmptyApp({
      prepare: async (dir) => {
        // copy extra resources
        await copy(resolve(appRoot(), 'test/resources/extra/har'), dir);
        await copy(resolve(appRoot(), 'test/resources/extra/har-packages/library.har'),
          resolve(dir, 'entry/library.har'));
        const entryPackageJson = new PackageJsonModifier(dir, 'entry');
        new ModuleBuildProfileModifier(dir, 'entry').updateTargetRuntimeOS('default', 'HarmonyOS');
        entryPackageJson.updateDependency('library', 'file:../library')
          .updateDependency('nlibrary', 'file:./library.har')
          .outputSync();
      },
      cb: async (dir) => {
        const commands = initExecuteCommand({
          mode: 'module',
          product: 'default',
          module: 'entry@default',
          tasks: 'assembleHap',
        });
        const result = runHvigor(dir, commands);

        // run successful
        expect(result.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();
        const probe = new Probes(dir, resolve(dir, 'entry'));
        const buildOutput = readdirSync(probe.getModuleBuildOutputPath());
        expect(buildOutput).toEqual([
          'entry-default-unsigned.hap',
          'entry-default-signed.hap',
          'pack.info'
        ].sort());

        // 检查.so文件存在情况
        const nativeOutput = readdirSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a'));
        expect(nativeOutput).toEqual([
          'libentry.so',
          'liblibrary.so',
          'libc++_shared.so',
          '1.so',
          '2.so'
        ].sort());
        expect(statSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a', '1.so')).size)
          .toBeGreaterThan(statSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a', '2.so')).size);
      },
      enableNative: true,
      runNpmInstall: true
    });
  });

  test.concurrent('Stage ets assembleHar', async () => {
    await initByStageEmptyApp(async (dir) => {
      // copy extra resources
      await copy(resolve(appRoot(), 'test/resources/extra/har'), dir);

      // this should not be copied
      writeFileSync(resolve(dir, 'test.har'), '');

      const commands = initExecuteCommand({
        mode: 'module',
        product: 'default',
        module: 'library@default',
        tasks: 'assembleHar',
      });
      new ModuleBuildProfileModifier(dir, 'entry').updateTargetRuntimeOS('default', 'HarmonyOS');
      const result = runHvigor(dir, commands);

      // run successful
      expect(result.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();
      const probe = new Probes(dir, resolve(dir, 'library'));
      const buildOutput = readdirSync(probe.getModuleBuildOutputPath());
      expect(buildOutput).toEqual([
        'library.har'
      ].sort());

      // 检查.so文件存在情况
      const nativeOutput = readdirSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a'));
      expect(nativeOutput).toEqual([
        'liblibrary.so',
        'libc++_shared.so',
        '1.so',
        '2.so',
        '3.so'
      ].sort());
    }, {
      enableNative: true
    });
  });

  test.concurrent('When multi library modules in project\'s package.json', async () => {
    await initByStageEmptyApp({
      prepare: async (dir) => {
        // copy extra resources
        await copy(resolve(appRoot(), 'test/resources/extra/har'), dir);
        new ModuleBuildProfileModifier(dir, 'entry').updateTargetRuntimeOS('default', 'HarmonyOS');
        new PackageJsonModifier(dir, '')
          .updateDependency('library', 'file:./library')
          .updateDependency('library1', 'file:./library1')
          .outputSync();
      },
      cb: async (dir) => {
        const commands = initExecuteCommand({
          mode: 'module',
          product: 'default',
          module: 'entry@default',
          tasks: 'assembleHap',
        });
        const result = runHvigor(dir, commands);

        // run successful
        expect(result.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();
        const probe = new Probes(dir, resolve(dir, 'entry'));
        const buildOutput = readdirSync(probe.getModuleBuildOutputPath());
        expect(buildOutput).toEqual([
          'entry-default-unsigned.hap',
          'entry-default-signed.hap',
          'pack.info'
        ].sort());

        // 检查.so文件存在情况
        const nativeOutput = readdirSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a'));
        expect(nativeOutput).toEqual([
          'libentry.so',
          'liblibrary.so',
          'libc++_shared.so',
          '1.so',
          '2.so'
        ].sort());
        expect(statSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a', '1.so')).size)
          .toBeGreaterThan(statSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a', '2.so')).size);
      },
      enableNative: true,
      runNpmInstall: true,
      nativeOption
    });
  });

  test.concurrent('When multi library include native resources', async () => {
    await initByStageEmptyApp({
      prepare: async (dir) => {
        // copy extra resources
        await copy(resolve(appRoot(), 'test/resources/extra/har'), dir);
        await copy(resolve(appRoot(), 'test/resources/extra/har-packages'), dir);
        new PackageJsonModifier(dir, '')
          .updateDependency('stage_ets_native_2', 'file:./stage_ets_native_2.har')
          .outputSync();
        new PackageJsonModifier(dir, 'library')
          .updateDependency('library1', 'file:../library1')
          .updateDependency('stage_ets_native', 'file:../stage_ets_native.har')
          .outputSync();
        new PackageJsonModifier(dir, 'entry')
          .updateDependency('library', 'file:../library')
          .updateDependency('stage_ets_native', 'file:../stage_ets_native.har')
          .outputSync();
      },
      cb: async (dir) => {
        const commands = initExecuteCommand({
          mode: 'module',
          product: 'default',
          module: 'entry',
          tasks: 'default@ProcessLibs',
        });
        new ModuleBuildProfileModifier(dir, 'entry').updateTargetRuntimeOS('default', 'HarmonyOS');
        const result = runHvigor(dir, commands);
        const probe = new Probes(dir, resolve(dir, 'entry'));

        const text = [
          resolve(dir,
            'oh_modules/.ohpm/stage_ets_native_2@file+stage_ets_native_2.har/oh_modules/stage_ets_native_2/libs'),
          resolve(dir,
            'library/oh_modules/.ohpm/stage_ets_native@file+..+stage_ets_native.har/oh_modules/stage_ets_native/libs'),
          resolve(dir, 'library1/build/default/intermediates/libs/default'),
          resolve(dir,
            'entry/oh_modules/.ohpm/stage_ets_native@file+..+stage_ets_native.har/oh_modules/stage_ets_native/libs'),
          resolve(dir, 'library/build/default/intermediates/libs/default'),
          resolve(dir, 'entry/build/default/intermediates/cmake/default/obj'),
          resolve(dir, 'entry/libs')
        ].toString().replace(/,/g, os.EOL);
        expect(result.stdout.includes(text)).toBeTruthy();

        // 检查.so文件存在情况
        const nativeOutput = readdirSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a'));
        expect(nativeOutput).toEqual([
          'libentry.so',
          'liblibrary.so',
          'libc++_shared.so',
          '1.so',
          '2.so',
          'libstage_ets_native_2.so',
          'libstage_ets_native.so'
        ].sort());
      },
      enableNative: true,
      runNpmInstall: true,
      nativeOption
    });
  });

  test.concurrent('When multi library include resources', async () => {
    await initByStageEmptyApp({
      prepare: async (dir) => {
        // copy extra resources
        await copy(resolve(appRoot(), 'test/resources/extra/har'), dir);
        await copy(resolve(appRoot(), 'test/resources/extra/har-packages'), dir);
        new PackageJsonModifier(dir, 'library')
          .updateDependency('library1', 'file:../library1')
          .updateDependency('stage_ets_native_2', 'file:../stage_ets_native_2.har')
          .outputSync();
        new PackageJsonModifier(dir, 'entry')
          .updateDependency('library', 'file:../library')
          .updateDependency('stage_ets_native', 'file:../stage_ets_native.har')
          .outputSync();
      },
      runNpmInstall: true,
      cb: async (dir) => {
        const commands = initExecuteCommand({
          mode: 'module',
          product: 'default',
          module: 'entry',
          tasks: 'default@CompileResource',
        });
        new ModuleBuildProfileModifier(dir, 'entry').updateTargetRuntimeOS('default', 'HarmonyOS');
        runHvigor(dir, commands, {
          stdio: 'inherit'
        });
        const probe = new Probes(dir, resolve(dir, 'entry'));

        const resConfig = readJsonSync(resolve(probe.getIntermediatesRes(), CommonConst.RES_CONFIG_FILE));
        expect(resConfig.dependencies).toEqual([
          resolve(dir, 'library', 'src', 'main', BuildDirConst.RESTOOL_BUILD_RESOURCES),
          resolve(dir, 'entry/oh_modules/.ohpm/stage_ets_native@file+..+stage_ets_native.har' +
            '/oh_modules/stage_ets_native/src/main', BuildDirConst.RESTOOL_BUILD_RESOURCES),
          resolve(dir, 'library1', 'src', 'main', BuildDirConst.RESTOOL_BUILD_RESOURCES),
          resolve(dir, 'library/oh_modules/.ohpm/stage_ets_native_2@file+..+stage_ets_native_2.har' +
            '/oh_modules/stage_ets_native_2/src/main', BuildDirConst.RESTOOL_BUILD_RESOURCES),
        ]);
      }
    });
  });

  test.concurrent('When local dependency in har - local har', async () => {
    await initByStageEmptyApp({
      prepare: async (dir) => {
        // copy extra resources
        await copy(resolve(appRoot(), 'test/resources/extra/har-packages'), dir);
        new PackageJsonModifier(dir, 'library')
          .updateDependency('stage_ets_native', '../stage_ets_native.har')
          .outputSync();
      },
      cb: async (dir) => {
        const commands = initExecuteCommand({
          mode: 'module',
          product: 'default',
          module: 'library@default',
          tasks: 'assembleHar',
        });
        runShouldFail(dir, commands, 'Local dependencies are not supported during HAR build');
      }
    });
  });

  test.concurrent('When local dependency in har - local library', async () => {
    await initByStageEmptyApp({
      prepare: async (dir) => {
        // copy extra resources
        await copy(resolve(appRoot(), 'test/resources/extra/har-packages'), dir);
        new PackageJsonModifier(dir, 'library')
          .updateDependency('library1', 'file:../library1')
          .outputSync();
      },
      cb: async (dir) => {
        const commands = initExecuteCommand({
          mode: 'module',
          product: 'default',
          module: 'library@default',
          tasks: 'assembleHar',
        });
        runShouldFail(dir, commands, 'Local dependencies are not supported during HAR build');
      }
    });
  });
});
